﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using AvalonDock;
using System.Windows.Controls;
using System.Windows;

namespace Skugo.Core
{
    public class ProjectClient : Client
    {
        /// <summary>
        /// The name of the project we're currently working on.
        /// </summary>
        public String Name;

        /// <summary>
        /// The directory to our user data.
        /// </summary>
        public DirectoryInfo UserDirectory;

        /// <summary>
        /// The path to the layout file data that we use for saving the UI.
        /// </summary>
        public String LayoutFile;

        /// <summary>
        /// The main docking panel that we use to dock windows.
        /// </summary>
        public DockingManager DockManager;

        /// <summary>
        /// The main grid that we attach content to for the form.
        /// </summary>
        public DockPanel Dock;

        /// <summary>
        /// A packet router that routes packets that come from the client
        /// to the individual plugins.
        /// </summary>
        public PacketRouter PacketRouter;

        /// <summary>
        /// Holds all the plugins that the remote host tells us to instantiate.
        /// </summary>
        public PluginContainer Plugins = new PluginContainer();

        /// <summary>
        /// Tells us whether the layout was loaded or not.
        /// </summary>
        private Boolean LayoutLoaded = false;

        public ProjectClient()
        {
            // The packet router routs packets to plugins that come from the client
            this.PacketRouter = new PacketRouter(this);

            // Create the dock panel and manager
            this.Dock = new DockPanel();
            this.DockManager = new DockingManager();
            this.DockManager.Loaded += this.OnDockManagerLoaded;

            // The dock manager is a child of the primary dock panel
            this.Dock.Children.Add(this.DockManager);

            // We want to know when we attempt to load any element
            // (and for some reason it cannot be loaded)
            this.DockManager.DeserializationCallback = (s, args) =>
            {
                // Create an invisible window in the same location
                var dockableContent = new DockableContent()
                {
                    Name = args.Name,
                    Title = args.Name,
                };

                // Tell the docking framework the window we created
                args.Content = dockableContent;
            };
        }

        void OnDockManagerLoaded(Object sender, RoutedEventArgs e)
        {
            this.AttemptRestoreLayout();
        }

        public override void Dispose()
        {
            base.Dispose();

            // Clear the plugins out
            this.Plugins.DestroyAll();
        }

        public void Setup(String name)
        {
            // Store away the project name
            this.Name = name;

            // Get the folder path to the application data directory
            var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);

            // Create a directory for skugo, and for the current project name
            this.UserDirectory = Directory.CreateDirectory(Path.Combine(appData, "Skugo", this.Name));

            // Get the layout file location
            this.LayoutFile = Path.Combine(this.UserDirectory.FullName, "Layout.xml");

            // Restore the last layout if one exists
            this.AttemptRestoreLayout();
        }

        public static void AttemptSaveLayout(ProjectClient client)
        {
            // As long as the client is valid, and connected, and we have a valid name...
            if (client != null &&
                client.State == ConnectionState.Connected &&
                client.Name != null)
            {
                // Save the layout!
                client.SaveLayout();
            }
        }

        private void SaveLayout()
        {
            // If we have no contents, don't bother saving!
            if (this.DockManager.DockableContents.Count == 0 || this.DockManager.IsLoaded == false)
            {
                return;
            }

            // NOTE:
            // We want to temporarily show hidden contents, since AvalonDock will actually save them,
            // but when we load the layout back up it does not seem to load them at all

            List<DockableContent> hiddenContents = new List<DockableContent>();

            var dockables = this.DockManager.DockableContents.ToList();

            // Loop through all the dockable contents
            foreach (var dock in dockables)
            {
                // If we have no content...
                if (dock.Content == null && dock.State == DockableContentState.Hidden)
                {
                    // Show the window temporarily
                    dock.Show();
                    hiddenContents.Add(dock);
                }
            }

            // Generate a temporary file that we save to
            // We do this to avoid cases where we crash / fail to fully save the file
            // and end up corrupting the layout (only allow good layouts to be saved!)
            var tempFile = Path.GetTempFileName();

            // Save the layout to a temporary file
            this.DockManager.SaveLayout(tempFile);

            // Restore any hidden contents back to being hidden
            foreach (var dock in hiddenContents)
            {
                dock.Hide();
            }

            // Copy the file over our layout file
            File.Copy(tempFile, this.LayoutFile, true);
        }

        private void AttemptRestoreLayout()
        {
            // As long as the dock is loaded, and the layout is not loaded, and we have a valid project name...
            if (this.DockManager.IsLoaded && !this.LayoutLoaded && this.Name != null)
            {
                try
                {
                    // Attempt to restore directly from the layout file
                    this.DockManager.RestoreLayout(this.LayoutFile);

                    // Loop through all the dockable contents
                    foreach (var dock in this.DockManager.DockableContents)
                    {
                        // If we have no content...
                        if (dock.Content == null)
                        {
                            // Hide this dock panel
                            dock.Hide();
                        }
                    }
                }
                catch
                {
                    try
                    {
                        // Otherwise, attempt to delete the layout file since it may be bad
                        File.Delete(this.LayoutFile);
                    }
                    catch
                    {
                    }
                }

                // We at least attempted to load the layout!
                this.LayoutLoaded = true;
            }
        }
    }
}
